package repository

import (
	"fmt"
	"oa-system/internal/model"
	"oa-system/pkg/errors"

	"gorm.io/gorm"
)

type RoleRepository interface {
	CreateRole(role *model.Role) (*model.Role, error)
	GetAllRoles(page, pageSize int, name string) ([]model.Role, int64, error)
	GetRoleByID(id uint) (*model.Role, error)
	UpdateRole(role *model.Role) (*model.Role, error)
	DeleteRole(id uint) error
	AssignMenusToRole(roleID uint, menuIDs []uint) error
	GetAssignedMenuIDs(roleID uint) ([]uint, error)
}

type roleRepository struct {
	db *gorm.DB
}

func NewRoleRepository(db *gorm.DB) RoleRepository {
	return &roleRepository{db: db}
}

func (r *roleRepository) CreateRole(role *model.Role) (*model.Role, error) {
	return role, r.db.Transaction(func(tx *gorm.DB) error {
		// 创建角色
		if err := tx.Create(role).Error; err != nil {
			return errors.NewInternalServerError("创建角色失败", err)
		}

		// 在字典表中创建对应的记录
		dictionary := model.Dictionary{
			Type:        "ROLE_TYPE",
			Label:       role.Name,
			Value:       fmt.Sprintf("%d", role.ID),
			Description: role.Description,
		}

		if err := tx.Create(&dictionary).Error; err != nil {
			return errors.NewInternalServerError("创建角色字典失败", err)
		}

		return nil
	})
}

func (r *roleRepository) GetAllRoles(page, pageSize int, name string) ([]model.Role, int64, error) {
	var roles []model.Role
	var total int64

	query := r.db.Model(&model.Role{})

	// 添加名称搜索条件
	if name != "" {
		query = query.Where("name LIKE ?", "%"+name+"%")
	}

	if err := query.Count(&total).Error; err != nil {
		return nil, 0, errors.NewInternalServerError("获取角色总数失败", err)
	}

	offset := (page - 1) * pageSize
	if err := query.Offset(offset).Limit(pageSize).Find(&roles).Error; err != nil {
		return nil, 0, errors.NewInternalServerError("获取角色列表失败", err)
	}

	return roles, total, nil
}

func (r *roleRepository) GetRoleByID(id uint) (*model.Role, error) {
	var role model.Role
	if err := r.db.First(&role, id).Error; err != nil {
		return nil, errors.NewNotFoundError("角色不存在", err)
	}
	return &role, nil
}

func (r *roleRepository) UpdateRole(role *model.Role) (*model.Role, error) {
	return role, r.db.Transaction(func(tx *gorm.DB) error {
		// 更新角色信息
		if err := tx.Model(role).Omit("created_at").Updates(role).Error; err != nil {
			return errors.NewInternalServerError("更新角色失败", err)
		}

		// 更新字典表中对应的记录
		if err := tx.Model(&model.Dictionary{}).
			Where("type = ? AND value = ?", "ROLE_TYPE", role.ID).
			Updates(map[string]interface{}{
				"label":       role.Name,
				"description": role.Description,
			}).Error; err != nil {
			return errors.NewInternalServerError("更新角色字典失败", err)
		}

		return nil
	})
}

func (r *roleRepository) DeleteRole(id uint) error {
	return r.db.Transaction(func(tx *gorm.DB) error {
		// 检查是否有用户关联此角色
		var count int64
		if err := tx.Model(&model.UserRole{}).Where("role_id = ?", id).Count(&count).Error; err != nil {
			return errors.NewInternalServerError("检查角色关联失败", err)
		}
		if count > 0 {
			return errors.NewBadRequestError("该角色下还有用户，无法删除", nil)
		}

		// 删除角色与菜单的关联
		if err := tx.Where("role_id = ?", id).Delete(&model.RoleMenu{}).Error; err != nil {
			return errors.NewInternalServerError("删除角色菜单关联失败", err)
		}

		// 删除角色与权限的关联
		if err := tx.Where("role_id = ?", id).Delete(&model.RolePermission{}).Error; err != nil {
			return errors.NewInternalServerError("删除角色权限关联失败", err)
		}

		// 删除角色
		if err := tx.Delete(&model.Role{}, id).Error; err != nil {
			return errors.NewInternalServerError("删除角色失败", err)
		}

		// 删除字典表中对应的记录
		if err := tx.Where("type = ? AND value = ?", "ROLE_TYPE", id).
			Delete(&model.Dictionary{}).Error; err != nil {
			return errors.NewInternalServerError("删除角色字典失败", err)
		}

		return nil
	})
}

func (r *roleRepository) AssignMenusToRole(roleID uint, menuIDs []uint) error {
	return r.db.Transaction(func(tx *gorm.DB) error {
		// 获取所有菜单
		var allMenus []model.Menu
		if err := tx.Find(&allMenus).Error; err != nil {
			return errors.NewInternalServerError("获取菜单列表失败", err)
		}

		// 创建菜单ID到菜单的映射
		menuMap := make(map[uint]*model.Menu)
		for i := range allMenus {
			menuMap[allMenus[i].ID] = &allMenus[i]
		}

		// 创建需要分配的菜单集合，包括父菜单
		menuSet := make(map[uint]struct{})
		for _, id := range menuIDs {
			menuSet[id] = struct{}{}
			// 添加所有父菜单
			for menu := menuMap[id]; menu != nil && menu.ParentID != nil; menu = menuMap[*menu.ParentID] {
				menuSet[*menu.ParentID] = struct{}{}
			}
		}

		// 删除现有的角色-菜单关联
		if err := tx.Where("role_id = ?", roleID).Delete(&model.RoleMenu{}).Error; err != nil {
			return errors.NewInternalServerError("删除现有菜单关联失败", err)
		}

		// 创建新的角色-菜单关联
		for menuID := range menuSet {
			if err := tx.Create(&model.RoleMenu{
				RoleID: roleID,
				MenuID: menuID,
			}).Error; err != nil {
				return errors.NewInternalServerError("创建菜单关联失败", err)
			}
		}

		return nil
	})
}

func (r *roleRepository) GetAssignedMenuIDs(roleID uint) ([]uint, error) {
	var menuIDs []uint
	if err := r.db.Model(&model.RoleMenu{}).
		Where("role_id = ?", roleID).
		Pluck("menu_id", &menuIDs).Error; err != nil {
		return nil, errors.NewInternalServerError("获取角色菜单失败", err)
	}
	return menuIDs, nil
}
